# -*- coding: utf-8 -*-
# @Time    : 2024/6/14 17:00
# @Author  : yujiahao
# @File    : 25_re_logging.py
# @description:re(正则)和logging(日志)


# todo 一、re模块


'''　
【什么是正则】
        正则就是用一些具有特殊含义的符号组合到一起（称为正则表达式）来描述字符或者字符串的方法。
    或者说：正则就是用来描述一类事物的规则。（在Python中）它内嵌在Python中，并通过 re 模块实现。
    正则表达式模式被编译成一系列的字节码，然后由用 C 编写的匹配引擎执行。

                ┌─────────┬───────────────────────────────────────────────────────────────────────────────┐
                │ 元字符   │ 匹配内容                                                                       │
                ├─────────┼───────────────────────────────────────────────────────────────────────────────┤
                │   \w    │ 匹配字母（包含中文）或数字或下划线                                                  │
                ├─────────┼───────────────────────────────────────────────────────────────────────────────┤
                │   \W    │ 匹配非字母（包含中文）或数字或下划线                                                │
                ├─────────┼───────────────────────────────────────────────────────────────────────────────┤
                │   \s    │ 匹配任意的空白符                                                                │
                ├─────────┼──────────────────────────────────────────────────────────────────────────────┤
                │   \S    │ 匹配任意非空白符                                                                │
                ├─────────┼──────────────────────────────────────────────────────────────────────────────┤
                │   \d    │ 匹配数字                                                                       │
                ├─────────┼───────────────────────────────────────────────────────────────────────────────┤
                │   \D    │ 匹配非数字                                                                      │
                ├─────────┼───────────────────────────────────────────────────────────────────────────────┤
                │   \A    │ 从字符串开头匹配                                                                 │
                ├─────────┼───────────────────────────────────────────────────────────────────────────────┤
                │   \z    │ 匹配字符串的结束，如果是换行，只匹配到换行前的结果                                    │
                ├─────────┼───────────────────────────────────────────────────────────────────────────────┤
                │   \n    │ 匹配一个换行符                                                                  │
                ├─────────┼───────────────────────────────────────────────────────────────────────────────┤
                │   \t    │ 匹配一个制表符                                                                  │
                ├─────────┼───────────────────────────────────────────────────────────────────────────────┤
                │   ^     │ 匹配字符串的开始                                                                 │
                ├─────────┼───────────────────────────────────────────────────────────────────────────────┤
                │   $     │ 匹配字符串的结尾                                                                 │
                ├─────────┼───────────────────────────────────────────────────────────────────────────────┤
                │   .     │ 匹配任意字符，除了换行符，当 re.DOTALL 标记被指定时，则可以匹配包括换行符的任意字符       │
                ├─────────┼───────────────────────────────────────────────────────────────────────────────┤
                │  [...]  │ 匹配字符组中的字符                                                               │
                ├─────────┼───────────────────────────────────────────────────────────────────────────────┤
                │ [^...]  │ 匹配除了字符组中的字符的所有字符                                                   │
                ├─────────┼───────────────────────────────────────────────────────────────────────────────┤
                │   *     │ 匹配 0 个或者多个左边的字符                                                       │
                ├─────────┼───────────────────────────────────────────────────────────────────────────────┤
                │   +     │ 匹配一个或者多个左边的字符                                                        │
                ├─────────┼───────────────────────────────────────────────────────────────────────────────┤
                │   ?     │ 匹配 0 个或者 1 个左边的字符，非贪婪方式                                            │
                ├─────────┼───────────────────────────────────────────────────────────────────────────────┤
                │  {n}    │ 精准匹配 n 个前面的表达式                                                         │
                ├─────────┼───────────────────────────────────────────────────────────────────────────────┤
                │ {n,m}   │ 匹配 n 到 m 次由前面的正则表达式定义的片段，贪婪方式                                  │
                ├─────────┼───────────────────────────────────────────────────────────────────────────────┤
                │   |     │ 匹配 a 或者 b                                                                  │
                ├─────────┼───────────────────────────────────────────────────────────────────────────────┤
                │   ()    │ 匹配括号内的表达式，也表示一个组                                                   │
                └─────────┴───────────────────────────────────────────────────────────────────────────────┘


'''

print("------------------------------------------------匹配模式----------------------------------------------------")

import re

name = ('''宇佳豪-yujiahao_123\t \n
宇家村_123 ''')

# \w 匹配中文,字母,数字,下划线
print('\w 匹配中文,字母,数字,下划线:', re.findall("\w", name))

# \W 不匹配中文,字母,数字,下划线
print('\W 不匹配中文,字母,数字,下划线:', re.findall('\W', name))

# \s 匹配任意的空白符
print('\s 匹配任意的空白符:', re.findall('\s', name))

# \S 匹配不是任意的空白符
print('\S 匹配不是任意的空白符:', re.findall('\S', name))

# \d 匹配数字
print('\d 匹配数字:', re.findall('\d', name))

# \D 匹配非数字
print('\D 匹配非数字:', re.findall('\D', name))

# \A 与 ^ 从字符串开头匹配
print('\A从字符串开头匹配:', re.findall('\A宇', name))
print('^ 匹配多行行首:', re.findall('^宇', name))

# \Z  与 $ 字符串结尾匹配
print('\Z字符串结尾匹配', re.findall('123 \Z', name))
print('$多行结尾匹配', re.findall('123 $', name, re.MULTILINE))

# \n 与 \t 匹配换行符、制表符
print('\\t匹配制表符', re.findall('\t', name))
print('\\n匹配换行符', re.findall('\n', name))

print('------------------------------------------------匹配方式----------------------------------------------------')

# . 匹配任意字符(换行符除外)
print('. 匹配任意字符(换行符除外)', re.findall(".", name))

# . 匹配任意字符
print('. 匹配任意字符', re.findall(".", name, re.DOTALL))

# ? 匹配?前元素0个或1个
print('? 匹配?前元素0个或1个', re.findall("宇?", name))

# * 匹配 * 前面元素0个或多个 [贪婪匹配]
print('* 匹配 * 前面元素0个或多个 [贪婪匹配]', re.findall(r"12*", name))

# + 匹配 + 前面元素 1 个或多个 [贪婪匹配]
print('+ 匹配 + 前面元素 1 个或多个 [贪婪匹配]:', re.findall(r"23+", name))

# {n,m} 匹配 n 到 m 个元素
print('{n,m} 匹配 n 到 m 个元素:', re.findall(r"1{1,2}", name))

# .* 任意内容 0 个或多个
print('.* 任意内容 0 个或多个:', re.findall(r".*", name))

# .*? 任意内容0个或1个
print('.*? 任意内容0个或1个:', re.findall(r"3*?2", name))

# [] 获取括号中的内容
name = "m-e-me-meet-meet_123"
print(re.findall("[1-9]", name))
# []中的-是什么至什么不会匹配-


print(re.findall("[a-z]", name))

print(re.findall("[A-z]", name))

# 是按照ascii码表位进行匹配的


name = "m-e-me-meet-meet_123"
print(re.findall("[a-zA-Z]", name))

import re

name = "m-e-me-meet-meet_123"
print(re.findall("[^A-z]", name))

# [^A-z] 有上尖号就是取反,获取不是字母和特定的几个字符

# 如果想要匹配到-,就需要进行如下操作(将-号放到最前面)

print(re.findall("[-+*/]", name))

# 练习

# 有如下字符串:'alex_sb ale123_sb wu12sir_sb wusir_sb ritian_sb' 的 alex wusir '

# 找到所有带_sb的内容

# () 分组 定制一个匹配规则


print(re.findall('(.*?)_sb', 'alex_sb wusir_sb 日天_sb'))

# 应用举例:
print(re.findall('href="(.*?)"', '<a href="http://www.baidu.com">点击</a>'))

print(re.findall('alex|宝元|wusir', 'alex宝元wusiraleeeex宝宝元odlb'))

print(re.findall('compan(day|morrow)', 'Work harder today than yesterday, and the day after tomorrow will be better'))

print(re.findall('compan(?:day|morrow)', 'Work harder today than yesterday, and the day after tomorrow will be better'))

# 分组() 中加入?: 表示将整体匹配出来而不只是()里面的内容。


print('------------------------------------------------常用方法----------------------------------------------------')

# findall 全部找到返回一个列表

print(re.findall("alex", "alexdsb,alex_sb,alexnb,al_ex"))

# search 从字符串中任意位置进行匹配查找到一个就停止了,返回的是一个对象. 获取匹配的内容必须使用.group()进行获取


print(re.search("sb|nb", "alexdsb,alex_sb,alexnb,al_ex").group())

# match 从字符串开始位置进行匹配


print(re.match('meet', 'meet alex wusir 日天').group())

print(re.match('alex', 'meet alex wusir 日天'))

# split 分隔 可按照任意分隔符进行分隔


print(re.split('[ ：:,;；，]', 'alex wusir,日天，太白;女神;肖锋：吴超'))

# sub 替换


print(re.sub('barry', 'meet', 'barry是最好的讲师，barry就是一个普通老师，请不要将barry当男神对待。'))

# meet是最好的讲师，meet就是一个普通老师，请不要将meet当男神对待。
# compile 定义匹配规则


obj = re.compile('\d{2}')
print(obj.findall("alex12345"))

obj = re.compile('\d{2}')
print(obj.search("alex12345").group())

# finditer 返回一个迭代器


g = re.finditer('al', "alex_alsb,al22,aladf")
print(next(g).group())
print([i.group() for i in g])

# 给分组起名字
ret = re.search("<(?P<tag_name>\w+)>\w+</\w+>", "<h1>hello</h1>")
print(ret.group("tag_name"))
print(ret.group())

ret = re.search(r"<(\w+)>\w+</\1>", "<h1>hello</h1>")
print(ret.group(1))
print(ret.group())

# 相关练习:

# 1 "1-2*(60+(-40.35/5)-(-4*3))"
# 1.1 匹配所有的整数
print(re.findall('\d+', "1-2*(60+(-40.35/5)-(-4*3))"))
# 1.2 匹配所有的数字（包含小数）
print(re.findall(r'\d+\.?\d*|\d*\.?\d+', "1-2*(60+(-40.35/5)-(-4*3))"))
# 1.3 匹配所有的数字（包含小数包含负号）
print(re.findall(r'-?\d+\.?\d*|\d*\.?\d+', "1-2*(60+(-40.35/5)-(-4*3))"))

# 2,匹配一段你文本中的每行的邮箱
# http://blog.csdn.net/make164492212/article/details/51656638 匹配所有邮箱

# 3，匹配一段你文本中的每行的时间字符串 这样的形式：'1995-04-27'

s1 = '''
时间就是1995-04-27,2005-04-27
1999-04-27 老男孩教育创始人
老男孩老师 alex 1980-04-27:1980-04-27
2018-12-08
'''
print(re.findall('\d{4}-\d{2}-\d{2}', s1))

# 4 匹配 一个浮点数
print(re.findall('\d+\.\d*', '1.17'))

# 5 匹配qq号：腾讯从10000开始：
print(re.findall('[1-9][0-9]{4,}', '2413545136'))

s1 = '''
<div id="cnblogs_post_body" class="blogpost-body"><h3><span style="font-family: 楷体;">python基础篇</span></h3>
<p><span style="font-family: 楷体;">&nbsp; &nbsp;<strong><a href="http://www.cnblogs.com/guobaoyuan/p/6847032.html" target="_blank">python 基础知识</a></strong></span></p>
<p><span style="font-family: 楷体;"><strong>&nbsp; &nbsp;<a href="http://www.cnblogs.com/guobaoyuan/p/6627631.html" target="_blank">python 初始python</a></strong></span></p>
<p><span style="font-family: 楷体;"><strong>&nbsp; &nbsp;<strong><a href="http://www.cnblogs.com/guobaoyuan/articles/7087609.html" target="_blank">python 字符编码</a></strong></strong></span></p>
<p><span style="font-family: 楷体;"><strong><strong>&nbsp; &nbsp;<a href="http://www.cnblogs.com/guobaoyuan/articles/6752157.html" target="_blank">python 类型及变量</a></strong></strong></span></p>
<p><span style="font-family: 楷体;"><strong>&nbsp; &nbsp;<a href="http://www.cnblogs.com/guobaoyuan/p/6847663.html" target="_blank">python 字符串详解</a></strong></span></p>
<p><span style="font-family: 楷体;">&nbsp; &nbsp;<strong><a href="http://www.cnblogs.com/guobaoyuan/p/6850347.html" target="_blank">python 列表详解</a></strong></span></p>
<p><span style="font-family: 楷体;"><strong>&nbsp; &nbsp;<a href="http://www.cnblogs.com/guobaoyuan/p/6850496.html" target="_blank">python 数字元祖</a></strong></span></p>
<p><span style="font-family: 楷体;">&nbsp; &nbsp;<strong><a href="http://www.cnblogs.com/guobaoyuan/p/6851820.html" target="_blank">python 字典详解</a></strong></span></p>
<p><span style="font-family: 楷体;"><strong>&nbsp; &nbsp;<strong><a href="http://www.cnblogs.com/guobaoyuan/p/6852131.html" target="_blank">python 集合详解</a></strong></strong></span></p>
<p><span style="font-family: 楷体;"><strong>&nbsp; &nbsp;<a href="http://www.cnblogs.com/guobaoyuan/articles/7087614.html" target="_blank">python 数据类型</a>&nbsp;</strong></span></p>
<p><span style="font-family: 楷体;"><strong>&nbsp; &nbsp;<a href="http://www.cnblogs.com/guobaoyuan/p/6752169.html" target="_blank">python文件操作</a></strong></span></p>
<p><span style="font-family: 楷体;"><strong>&nbsp; &nbsp;<a href="http://www.cnblogs.com/guobaoyuan/p/8149209.html" target="_blank">python 闭包</a></strong></span></p>
<p><span style="font-family: 楷体;"><strong>&nbsp; &nbsp;<a href="http://www.cnblogs.com/guobaoyuan/articles/6705714.html" target="_blank">python 函数详解</a></strong></span></p>
<p><span style="font-family: 楷体;"><strong>&nbsp; &nbsp;<a href="http://www.cnblogs.com/guobaoyuan/articles/7087616.html" target="_blank">python 函数、装饰器、内置函数</a></strong></span></p>
<p><span style="font-family: 楷体;"><strong>&nbsp; &nbsp;<a href="http://www.cnblogs.com/guobaoyuan/articles/7087629.html" target="_blank">python 迭代器 生成器</a>&nbsp;&nbsp;</strong></span></p>
<p><span style="font-family: 楷体;"><strong>&nbsp; &nbsp;<a href="http://www.cnblogs.com/guobaoyuan/articles/6757215.html" target="_blank">python匿名函数、内置函数</a></strong></span></p>
</div>
'''
# 1,找到所有的span标签的内容
ret = re.findall('<span(.*?)>', s1)
print(ret)

# 2,找到所有a标签对应的url
print(re.findall('<a href="(.*?)".*?</a>', s1))

print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

# todo 二、logging模块


'''Python 的 logging 模块是一个用于记录日志的标准库模块。它提供了一种灵活的框架，用于在应用程序中记录各种信息，
如调试信息、错误信息、警告信息等。logging 模块支持多种日志记录方式，包括将日志输出到控制台、文件、网络等。

基本概念
    - Logger: 日志记录器，负责生成日志记录。
    - Handler: 处理器，负责将日志记录发送到指定的输出位置，如文件、控制台等。
    - Formatter: 格式化器，负责指定日志记录的输出格式。
    - Filter: 过滤器，提供更细粒度的控制来决定哪些日志记录会被输出。

日志级别

默认情况下Python的logging模块将日志打印到了标准输出中，且只显示了大于等于WARNING级别的日志，这说明默认的日志级别设置为WARNING

logging 模块定义了以下几个日志级别，从低到高依次是：

    - DEBUG: 调试信息，详细的信息，通常只在诊断问题时使用。
    - INFO: 一般信息，确认程序正常运行。
    - WARNING: 警告信息，表示某些情况可能会有问题或潜在的问题。
    - ERROR: 错误信息，表示程序在某些情况下不能正常运行。
    - CRITICAL: 严重错误，表示程序可能无法继续运行。

'''

import logging

# 配置日志记录
logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S')

# 创建一个日志记录器
logger = logging.getLogger(__name__)

# todo 1、函数式简单配置


logging.debug('这是调试信息')
logging.info('这是信息')
logging.warning('这是警告')
logging.error('这是错误')
logging.critical('这是严重错误')

# todo 2、灵活配置日志级别，日志格式，输出位置:


'''

basicConfig()函数中可通过具体参数来更改logging模块默认行为，可用参数有：

    filename：用指定的文件名创建FiledHandler，这样日志会被存储在指定的文件中。
    filemode：文件打开方式，在指定了filename时使用这个参数，默认值为“a”还可指定为“w”。
    format：指定handler使用的日志显示格式。
        
        format参数中可能用到的格式化串：
            %(name)s Logger的名字
            %(levelno)s 数字形式的日志级别
            %(levelname)s 文本形式的日志级别
            %(pathname)s 调用日志输出函数的模块的完整路径名，可能没有
            %(filename)s 调用日志输出函数的模块的文件名
            %(module)s 调用日志输出函数的模块名
            %(funcName)s 调用日志输出函数的函数名
            %(lineno)d 调用日志输出函数的语句所在的代码行
            %(created)f 当前时间，用UNIX标准的表示时间的浮 点数表示
            %(relativeCreated)d 输出日志信息时的，自Logger创建以 来的毫秒数
            %(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
            %(thread)d 线程ID。可能没有
            %(threadName)s 线程名。可能没有
            %(process)d 进程ID。可能没有
            %(message)s用户输出的消息
    
    
    datefmt：指定日期时间格式。
    level：设置记录日志的级别
    stream：用指定的stream创建StreamHandler。可以指定输出到
    sys.stderr,sys.stdout或者文件(f=open(‘test.log’,’w’))，默认为sys.stderr。若同时列出了filename和stream两个参数，则stream参数会被忽略。
'''

logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
                    datefmt='%a, %d %b %Y %H:%M:%S',
                    filename='test2.log',
                    filemode='w')

logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')

# todo 3、 logger对象配置


'''
logging库提供了多个组件：Logger、Handler、Filter、Formatter。
Logger对象提供应用程序可直接使用的接口，Handler发送日志到适当的目的地，Filter提供了过滤日志信息的方法，Formatter指定日志显示格式。
另外，可以通过：logger.setLevel(logging.Debug)设置级别,当然，也可以通过

fh.setLevel(logging.Debug)单对文件流设置某个级别。
'''

# 创建日志记录器
logger = logging.getLogger()

# 创建一个handler，用于写入日志文件
fh = logging.FileHandler('test.log', encoding='utf-8')

# 再创建一个handler处理器，用于输出到控制台
ch = logging.StreamHandler()

# 创建格式化器
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# 设置处理器的日志级别
fh.setLevel(logging.DEBUG)

# 将格式化器添加到处理器
fh.setFormatter(formatter)
ch.setFormatter(formatter)

# 将处理器添加到日志记录器
logger.addHandler(fh)  # logger对象可以添加多个fh和ch对象
logger.addHandler(ch)

# 记录日志
logger.debug('logger debug message')
logger.info('logger info message')
logger.warning('logger warning message')
logger.error('logger error message')
logger.critical('logger critical message')
